/*
 * Written by edhong Feb. 2008
 *  sample arithmetic DECODER using modified QccPack,
 *  decodes a textfile consisting of only (ascii) 0's and 1's. 
 * Feb 2008
 *
 * This file is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * 
 * This file is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 * 
 * 
 */

#include "libQccPack.h" 
#define BLOCK_LENGTH 1
#define USG_STRING "[-eof %d:EncodeEOF=0] [-tk %d:Use_tkfreq=1]  [-n %d:numContexts=1]  [-b %d:numBits] %s:inbitsfile %s:outtbitsfile"


int encode_eof = 0;
int use_tkfreq = 1;
int numContexts = 1;  // # contexts to use; decode the same input file in every context. 
int numBits = -1;    // # (input) bits to decode; -1 == decode until EOF, must be present when EncodeEOF==0.
QccString inbitsfile;
QccString outtbitsfile;

/* this function is for  making the error checking in main more readable */
void checkerror(int error, const char *message) {
  if (error) {
    char errormessage[1000];
    snprintf(errormessage, 1000, "Error Calling %s()", message); 
    QccErrorAddMessage(errormessage);
    QccErrorExit(); 
  } 
}

int main(int argc, char *argv[]) 
{ 
QccInit(argc, argv);

  if (QccParseParameters(argc, argv,
                         USG_STRING,
                         &encode_eof,
                         &use_tkfreq,
			 &numContexts,
			 &numBits,
                         &inbitsfile,
                         &outtbitsfile))
    QccErrorExit();
  
  if (encode_eof < 0  || encode_eof >  1)
    {
      QccErrorAddMessage("%s: encode_eof  must be 0 or 1",
                         argv[0]);
      QccErrorExit();
    }

  if (use_tkfreq < 0  || use_tkfreq >  1)
    {
      QccErrorAddMessage("%s: use_tkfreq  must be 0 or 1",
                         argv[0]);
      QccErrorExit();
    }

  if (numContexts <= 0 )
    {
      QccErrorAddMessage("%s: numContexts  must be >0",
                         argv[0]);
      QccErrorExit();
    }

  if (numBits < -1 )
    {
      QccErrorAddMessage("%s: numBits  must be >=-1 (use -1 for allbits)",
                         argv[0]);
      QccErrorExit();
    }
  else if (numBits == -1 && encode_eof == 0) {
    QccErrorAddMessage("%s: numBits must be >=0 when EOF is not encoded",
		       argv[0]);
    QccErrorExit();
  }

  if (!QccFileExists(inbitsfile)) {
    QccErrorAddMessage("%s: cannot find file %s ",
		       argv[0], inbitsfile);
    QccErrorExit();
  }

  QccString WRITEMODE;
  QccConvertToQccString(WRITEMODE, "w");
  FILE *outtbitsfp = QccFileOpen(outtbitsfile, WRITEMODE);

  QccBitBuffer input_buffer; 
  QccENTArithmeticModel *model = NULL; 

  int *num_symbols;
  num_symbols = (int *)malloc(sizeof(int)*numContexts);
  int i;
  for ( i=0; i < numContexts; i++) {
    num_symbols[i] = 2;
  }
  
  QccBitBufferInitialize(&input_buffer); 
  QccConvertToQccString(input_buffer.filename, inbitsfile);

  input_buffer.type = QCCBITBUFFER_INPUT; 
  checkerror(QccBitBufferStart(&input_buffer), "QccBitBufferStart");

  model = QccENTArithmeticDecodeStart2(&input_buffer, 
				       num_symbols, 
				       numContexts, 
				       NULL, 
				       QCCENT_ANYNUMBITS,
				       encode_eof);

  checkerror(model == NULL, "QccENTArithmeticDecodeStart2");

  if (use_tkfreq) 
    QccENTArithmeticSetModelAdaption(model, QCCENT_TKADAPTIVE);
  else
    QccENTArithmeticSetModelAdaption(model, QCCENT_ADAPTIVE);

  printf("Decoding from %s into %s.., %d contexts, %d bits, ", 
	 inbitsfile, outtbitsfile, numContexts, numBits);
  if (encode_eof) printf("use_eof, ");
  else printf("no_eof, ");
  
  if (use_tkfreq) printf("use_tkfreq\n");
  else printf("normal_adapt\n");
  
  int x;
  int decodecount[2] = {0,0};

  int done = 0;
  for ( x =0; !done; x++) {
    if (x >= numBits) break; 

    int counter;
    int errorvalue; 
    int decodeval;
    for (counter = 0; counter < numContexts; counter++) {
      errorvalue = QccENTArithmeticSetModelContext(model, counter);
      checkerror(errorvalue, "QccENTArithmeticSetModelContext");
 
      errorvalue = QccENTArithmeticDecode(&input_buffer, 
					  model, 
					  &decodeval, 
					  BLOCK_LENGTH); 

      if (errorvalue == 2) {
	printf("EOF found, last symbol was %d\n", decodeval);
	done = 1;
	break;
      }
      else if (errorvalue == 1) {
        printf("Error decoding next symbol;  last symbol was %d\n", decodeval);	
	done =1;
	break;
      }
      else {
	//	printf("%d\n", decodeval);
	QccFileWriteChar(outtbitsfp, (char)decodeval+'0');
	decodecount[decodeval]++;
	if (decodecount[0]+decodecount[1] >= numBits) {
	  done = 1;
	  break;
	}
      }
    }
  }
  
  checkerror(QccFileClose(outtbitsfp), "QccFileClose");

  double total = decodecount[0]+decodecount[1];
  printf("Decoded 1-bits: %d, 0-bits: %d, total: %d, prob: %g\n",
	 decodecount[1], decodecount[0], (int) total, decodecount[1]/total);
  
  QccENTArithmeticFreeModel(model); 

  checkerror(QccBitBufferEnd(&input_buffer), 
	     "QccBitBufferEnd");

  printf("BitBuffer bit_count: %d\n", input_buffer.bit_cnt);

  QccExit; 
} 
